/*
 * @include "/js-libs/canvas-doc.js"
 * @include "/ictinus/src/ictinus.js"
 * @include "/ictinus/src/ictinus.drawItems.js"
 * @include "/ictinus/src/ictinus.Shape.js"
 * @include "/ictinus/src/ictinus.Shape.pathData.js"
 */
 
/**
 * Декоратор картинок. Использует Canvas или VML для рисования декораций вокруг
 * картинок, например, уголки причудливой формы. 
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 */
var ictinus = (function(){
	var userAgent = navigator.userAgent.toLowerCase();

	/** @type {Boolean} Говорит, что текущий браузер — Internet Explorer */
	var is_msie = /msie/.test( userAgent ) && !/opera/.test( userAgent );
	
	/**
	 * Проверяет, существует ли указанный класс у элемента
	 * @param {HTMLElement} elem
	 * @param {String} className
	 */
	function hasClass(elem, className){
		return elem.nodeType == 1 && elem.className.indexOf(className) != -1;
	}
	
	/**
	 * Адаптер для рисования через canvas
	 */
	function canvasAdapter(){
		/**
		 * Создает холст для рисования для переданного объекта. Элемент холста
		 * будет добавлен <i>перед</i> элементом <code>elem</code>, если элемент
		 * является картинкой (<code>tagName == 'IMG'</code>), либо добавлен 
		 * в сам элемент. Если холст уже был создан, он заново не создается, 
		 * а возвращается ссылка на уже созданный
		 * @param {Element} elem Элемент, для которого нужно создать холст
		 * @return {CanvasRenderingContext2D}
		 */
		function createCanvas(elem){
			/** @type {HTMLCanvasElement} */
			var canvas;
			
			if (elem.className.indexOf('ictinus-init') != -1) {
				// ищем созданный холст
				if (elem.nodeName == 'IMG' && elem.previousSibling.nodeName == 'CANVAS') {
					canvas = elem.previousSibling;
				} else if (elem.getElementsByTagName('canvas').length) {
					canvas = elem.getElementsByTagName('canvas')[0];
				}
			}
			
			if (!canvas) {
				canvas = document.createElement('canvas');
				
				// поправка в 1 пиксель нужна для того, чтобы точнее рисовались нечетные линии
				canvas.width = elem.offsetWidth + 1;
				canvas.height = elem.offsetHeight + 1;
				canvas.className = 'ictinus';
				
				if (elem.tagName == 'IMG') {
					elem.parentNode.insertBefore(canvas, elem);
				} else {
					elem.appendChild(canvas);
				}
				
				elem.className = (elem.className ? elem.className + ' ' : '') + 'ictinus-init';
			}
			return canvas.getContext('2d');
		}
		
		/**
		 * Создает контур на холсте
		 * @param {CanvasRenderingContext2D} ctx
		 * @param {ictinus.Shape} shape
		 */
		function createPath(ctx, shape){
			ctx.beginPath();
			
			shape.traverse(function(/* ictinus.drawItems.Item */ item){
				item.drawCanvas(ctx);
			});
			
			ctx.closePath();
		}
		
		/**
		 * Рисует вспомогательные линии для кривых. Полезен для отладки внешнего
		 * вида формы.
		 * @param {CanvasRenderingContext2D} ctx
		 * @param {ictinus.Shape} shape
		 */
//		function drawHull(ctx, shape){
//			// ставим новые параметры рисования для вспомогательных линий
//			ctx.fillStyle = '#ffcc00';
//			ctx.lineWidth = 1;
//			ctx.strokeStyle = '#00ff00';
//	
//			/**
//			 * Вспомогательный метод для рисования точки
//			 * @param {Number} x 
//			 * @param {Number} y 
//			 * @param {Number} size Размер точки в пикселях
//			 */
//			function drawPoint(x, y, size){
//				ctx.fillRect(x - size / 2 , y - size / 2, size, size);
//			};
//			
//			function drawConnector(from_x, from_y, to_x, to_y){
//				ctx.beginPath();
//				ctx.moveTo(from_x, from_y);
//				ctx.lineTo(to_x, to_y);
//				ctx.stroke();
//				ctx.closePath();
//			}
//
//			var paths = shape.getPaths();
//			ctx.beginPath();
//			
//			shape.traverse(function(/* ictinus.drawItems.Item */ item){
//				ctx.moveTo(item.x, item.y);
//				drawPoint(item.x, item.y, 6);
//				
//				if (item.typeOf('bezier')) {
//					drawConnector(item.x, item.y, item.cpx2, item.cpy2);
//					drawConnector(item.prev().x, item.prev().y, item.cpx1, item.cpy1);
//					drawPoint(item.cpx1, item.cpy1, 2);
//					drawPoint(item.cpx2, item.cpy2, 2);
//					
//					ctx.moveTo(item.x, item.y);
//				}
//			});
//		};
		
		/**
		 * Готовит холст для рисования: меняет ему размеры под габариты формы, 
		 * ставит указатель рисования в нужную точку и т.д.
		 * @param {CanvasRenderingContext2D} ctx
		 * @param {ictinus.drawItems.Item} shape
		 */
		function prepareCanvas(ctx, shape) {
			ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
			
			// меняем размеры холста
			ctx.canvas.width = Math.ceil(shape.width() + shape.paddingLeft + shape.paddingRight + shape.strokeWidth * 2);
			ctx.canvas.height = Math.ceil(shape.height() + shape.paddingTop + shape.paddingBottom + shape.strokeWidth * 2);
			
			ctx.save();
			var offset = Math.floor(shape.strokeWidth / 2);
			ctx.translate(shape.paddingLeft + offset, shape.paddingTop + offset);
		}
		
		return {
			/**
			 * Рисует декорации для картинки
			 * @param {Element} img Картинка, для которой нужно нарисовать декорации
			 * @param {ictinus.Shape} shape Форма декорации (массив, состоящий из объектов типа line())
			 */
			decorate: function(img, shape){
				var ctx = createCanvas(img);
				
				// FIXME это быстрый хак, чтобы рисовались линии нечетной толщины, придумать более надежный механизм
				var offset = (shape.strokeWidth % 2) / 2;
				
				prepareCanvas(ctx, shape);
				
				// сначала рисую форму декорации, которая будет служить контуром отсечения
				ctx.save();
				
				createPath(ctx, shape);
				ctx.clip();
				
				if (shape.fillColor) {
					ctx.rect(0, 0, img.offsetWidth, img.offsetHeight);
					ctx.fillStyle = shape.fillColor;
					ctx.fill();
				}
				
				ctx.translate(shape.contentBox.x, shape.contentBox.y);
//				alert('draw');
				ctx.drawImage(img, 0, 0, img.offsetWidth, img.offsetHeight);
				ctx.restore();
				
				
				if (shape.strokeWidth) {
					// еще раз создам контур, чтобы нормально отработал stroke в Сафари
					ctx.save();
					// делаю небольшую поправку для координат, чтобы 
					// прямые линии не были смазанными
					ctx.translate(offset, offset);
					
					createPath(ctx, shape);
					ctx.strokeStyle = shape.strokeColor;
					ctx.lineWidth = shape.strokeWidth;
					ctx.stroke();
					ctx.restore();
				}
				ctx.restore();
				
//				drawHull(ctx, shape);
			},
			
			/**
			 * Рисует форму
			 * @param {Element} target Элемент, в который нужно добавить холст
			 * @param {ictinus.Shape} shape Форма, которую нужно нарисовать
			 */
			draw: function(target, shape){
				var ctx = createCanvas(target);
				
				prepareCanvas(ctx, shape);
				
				createPath(ctx, shape);
				ctx.fillStyle = shape.fillColor;
				ctx.fill();
				
				if (shape.strokeWidth) {
					ctx.strokeStyle = shape.strokeColor;
					ctx.lineWidth = shape.strokeWidth;
					ctx.stroke();
				}
				
				ctx.restore();
			}
		}
	};
	
	/**
	 * Адаптер для рисования через VML
	 */
	function vmlAdapter(){
		/** Повышающий коэффициент для координатного пространства */
		var multiplier = 10;
		
		// XXX разобраться, почему тут стояло значение 1
		// FIXME разобрался — влияет толщина обводки, доделать
		var offset = 0;
		
		/**
		 * Исправляет форму рисования.
		 * Похоже, что VML в IE не поддерживает дробные коэффициенты,
		 * потому что сейчас они кардинально меняют отрисованную форму.
		 * Этот метод умнажает каждую координату формы рисования на 
		 * <code>multiplier</code> и округляет ее, тем самым избавляет линии
		 * от дробных коэффициентов.
		 * Надо помнить, что просле применения этого метода нужно в том числе
		 * изменить координатное пространство элемента shape, умножив его 
		 * габариты на <code>multiplier</code>
		 * @param {ictinus.Shape} shape Форма рисования
		 * @param {Array} Новый набор инструкций для рисования
		 */
		function getFixedPaths(shape){
			// из shape.getPaths() к нам могут прийти сессионные пути — это
			// будет именно ссылка на объект, который нам ни в коем случае менять 
			// нельзя, поэтому делаю дубликаты
			var new_paths = [];
			
			shape.traverse(function(/* ictinus.drawItems.Item */ item){
				var clone = item.clone();
				
				// не забываем проставлять ссылки на следующий/предыдущмй элементы
				if (new_paths.length) {
					new_paths[new_paths.length - 1].next(clone);
					clone.prev(new_paths[new_paths.length - 1]);
				}
				
				clone.walkOnCoords(function(prop){
					this[prop] = Math.round(this[prop] * multiplier);
				});
				new_paths.push(clone);
			}, shape.getPaths());
			
			return new_paths;
		}
		
		if (!document.namespaces["v"]) {
			document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
			// setup default css
			var ss = document.createStyleSheet();
			ss.cssText = "v\\:* {behavior:url(#default#VML);display:block;}";
		}
		
		/**
		 * Создает строку, описывающую путь в формате path data
		 * @param {ictinus.Shape} shape
		 * @return {String}
		 */
		function createPathString(shape){
			var path = [];
			shape.traverse(function(/* ictinus.drawItems.Item */ item){
				path.push(item.drawVML());
			}, getFixedPaths(shape));
			
			path.push('x e');
			
			return path.join(' ');
		}
		
		/**
		 * Создает импровизированный холст для рисования для переданного объекта. 
		 * Холст представляет собой элемент div с классом ictinus
		 * Элемент холста будет добавлен <i>перед</i> элементом <code>elem</code>
		 * @param {Element} elem Элемент, для которого нужно создать холст
		 * @return {HTMLElement}
		 */
		function createCanvas(elem){
			/** @type {HTMLElement} */
			var canvas;
			
			if (elem.className.indexOf('ictinus-init') != -1) {
				// ищем созданный холст
				if (elem.nodeName == 'IMG' && hasClass(elem.previousSibling, 'ictinus')) {
					canvas = elem.previousSibling;
				} else if (elem.getElementsByTagName('div').length) {
					var children = elem.getElementsByTagName('div');
					for (var i = 0; i < children.length; i++) {
						if (hasClass(children[i], 'ictinus')) {
							canvas = children[i];
							break;
						}
					}
				}
			}
			
			if (!canvas) {
				canvas = document.createElement('div');
				
				canvas.style.width = elem.offsetWidth + 'px';
				canvas.style.height = elem.offsetHeight + 'px';
				canvas.className = 'ictinus';
				
				if (elem.tagName == 'IMG') {
					elem.parentNode.insertBefore(canvas, elem);
				} else {
					elem.appendChild(canvas);
				}
				
				elem.className = (elem.className ? elem.className + ' ' : '') + 'ictinus-init';
			}
			return canvas;
		}
		
		return {
			/**
			 * Рисует декорации для картинки
			 * @param {Element} img Картинка, для которой нужно нарисовать декорации
			 * @param {ictinus.Shape} shape Форма декорации
			 * @param {Object} params Параметры декорации: цвет (line_color) и толщина (line_width) линии
			 */
			decorate: function(img, shape){
				
				/** @type {Element} */
				var fill;
				
				this.draw(img, shape);
				
				/** @type {HTMLElement} */
				var elem = shape._cache.shape_elem
				
				var cached = Boolean(shape._cache.fill_elem);
				
				if (cached) {
					fill = shape._cache.fill_elem;
				} else {
					// создаю заливку формы				
					fill = document.createElement('v:fill');
					fill.type = 'tile';
					elem.appendChild(fill);
					shape._cache.fill_elem = fill;
				}
				
				/*
				 * очень важно, чтобы координаты определялись до того, как будет
				 * присвоен src элементу, иначе IE будет периодически тупить
				 * и возвращать неправильные значения
				 */
				fill.origin = ((elem.offsetLeft + offset - shape.paddingLeft) / img.offsetWidth) + ' ' + ((elem.offsetTop + offset - shape.paddingTop) / img.offsetHeight);
				fill.size = img.width + 'px,' + img.height + 'px';
				fill.src = img.src;
			},
			
			/**
			 * Рисует форму
			 * @param {Element} target Элемент, в который нужно добавить холст
			 * @param {ictinus.Shape} shape Форма, которую нужно нарисовать
			 */
			draw: function(target, shape){
				// TODO добавить поддержку полупрозрачности в fillColor
				
				/** @type {Element} */
				var elem;
				
				/** @type {HTMLElement} */
				var cv;
				
				var cached = Boolean(shape._cache.shape_elem);
				
				if (cached) {
					elem = shape._cache.shape_elem;
				} else {
					// создаю элемент контура
					elem = document.createElement('v:shape');
					cv = createCanvas(target);
					shape._cache.shape_elem = elem;
				}
				
				if (!shape.strokeWidth) {
					elem.stroked = false;
				} else {
					elem.strokeweight = shape.strokeWidth;
					elem.strokecolor = shape.strokeColor;
				}
				
				elem.fillcolor = shape.fillColor;
				
				
				var w = shape.width() + shape.paddingLeft + shape.paddingRight;
				var h = shape.height() + shape.paddingTop + shape.paddingBottom;
				
				elem.style.width = w + 'px';
				elem.style.height = h + 'px';
				elem.coordorigin = (-shape.paddingLeft * multiplier) + ' ' + (-shape.paddingTop * multiplier);
				elem.coordsize = (w * multiplier + offset) + ' ' + (h * multiplier + offset);
				
				
				elem.path = createPathString(shape);
				
				if (!cached) {
					cv.appendChild(elem);
				}
			}
		}

	};
	
	var painter = is_msie ? vmlAdapter() : canvasAdapter();
	
	return {
		/**
		 * Рисует декорации для картинки
		 * @param {Element} img Картинка, для которой нужно нарисовать декорации
		 * @param {ictinus.Shape} shape Форма рисования
		 */
		decorate: function(img, shape){
			painter.decorate(img, shape);
		},
		
		/**
		 * Рисует форму <code>shape</code> внутри элемента
		 * @param {Element} target Контейнер, в котором нужно рисовать
		 * @param {ictinus.Shape} shape Форма, которую нужно нарисовать
		 */
		draw: function(target, shape){
			painter.draw(target, shape);
		}
	};
})();